//Author: Ferdinand Eisenkeil, Soeren Pirk
//Date  : 23.04.2014

#include "Object.h"
#include "VertexBufferObjectAttribs.h"
#include "Shader.h"
#include "ModelObj.h"

#include <fstream>

Object::Object()
: m_position(0.0f, 0.0f, 0.0f),
  m_scale(0.0f, 0.0f, 0.0f)
{    
    init();
}

Object::Object(vec3 pos, vec3 scale, char *file)
: m_position(pos),
  m_scale(scale),
  m_file(file)
{    
    init();
}

Object::~Object()
{
}

void Object::init()
{
    buildVBO();

	m_shader = new Shader("../Shader/Default33.vert.glsl", "../Shader/Default33.frag.glsl");

	culling = true;
}

void Object::render(const Transform &trans)
{
    mat4 projection = trans.projection;
    mat4 view = trans.view;
    mat4 model = mat4::identitiy();

    glPushAttrib(GL_ALL_ATTRIB_BITS);
    glPushClientAttrib(GL_CLIENT_ALL_ATTRIB_BITS);

    vec3 camPos = getCamPosFromModelView(trans);
	
    m_shader->bind();
	
        model = mat4::translate(m_position) * mat4::scale(m_scale);
        mat4 normal = mat3(view * model).inverse().transpose();

        m_shader->setMatrix("matProjection", projection, GL_TRUE);
        m_shader->setMatrix("matView", view, GL_TRUE);
        m_shader->setMatrix("matModel", model, GL_TRUE); 
		m_shader->set3f("camPos", camPos);
		m_shader->setMatrix("matNormal", normal);
		m_shader->seti("culling", (int)culling);
        
        for(int i=0; i<m_vbosSolid.size(); ++i)
		{
            m_vbosSolid[i]->render();
		}
        
    m_shader->release();
	
    glPopClientAttrib();
    glPopAttrib();
}

void Object::buildVBO()
{
    ModelOBJ *model = new ModelOBJ();	
	model->import(m_file);
    
	int nrMeshes = model->getNumberOfMeshes();
    int nrVertices = model->getNumberOfVertices();

    const ModelOBJ::Vertex *vb = model->getVertexBuffer();
    const int *ib = model->getIndexBuffer();
    

	for(int i = 0; i < nrMeshes; ++i)	
	{
		std::vector<vec3> tmpVertices;	
		std::vector<vec3> tmpColor;	
		std::vector<vec3> tmpNormals;
		std::vector<vec3> tmpTexCoords;

		const ModelOBJ::Mesh &objMesh = model->getMesh(i);
		const int startIndex = objMesh.startIndex;
		const int triangleCount = objMesh.triangleCount;
		const int vertexCount = objMesh.triangleCount * 3;

        for(int j=0; j<nrVertices; ++j)
		{
            ModelOBJ::Vertex v = vb[j];

			vec3 p(v.position[0], v.position[1], v.position[2]);
			vec3 c(v.color[0], v.color[1], v.color[2]);
			vec3 n(v.normal[0], v.normal[1], v.normal[2]);
			vec3 t(v.texCoord[0], v.texCoord[1], 0.0f);  
			
			tmpVertices.push_back(p);
			tmpColor.push_back(c);
			tmpNormals.push_back(n);
			tmpTexCoords.push_back(t);
        }        

		VertexBufferObjectAttribs::DATA *data = new VertexBufferObjectAttribs::DATA[tmpVertices.size()];

		for(int i=0; i<tmpVertices.size(); ++i)
		{
			vec3 pos = tmpVertices[i];
			vec3 c =   tmpColor[i];
			vec3 n   = tmpNormals[i];
			vec3 t   = tmpTexCoords[i];

			data[i].vx = pos.x;
			data[i].vy = pos.y;
			data[i].vz = pos.z;
			data[i].vw = 1.0f;

			data[i].cx = c.x;
			data[i].cy = c.y;
			data[i].cz = c.z;
			data[i].cw = 1.0;

			data[i].nx = n.x;
			data[i].ny = n.y;
			data[i].nz = n.z;
			data[i].nw = 1.0f;

			data[i].tx = t.x;
			data[i].ty = t.y;
            data[i].tz = 0.0f;
            data[i].tw = 0.0f;
		}

		VertexBufferObjectAttribs* vboMesh = new VertexBufferObjectAttribs();
		vboMesh->setData(data, GL_STATIC_DRAW, tmpVertices.size(), GL_TRIANGLES); 
        vboMesh->setIndexData(ib, GL_STATIC_DRAW, startIndex + vertexCount);

		vboMesh->addAttrib(VERTEX_POSITION);
		vboMesh->addAttrib(VERTEX_NORMAL);
		vboMesh->addAttrib(VERTEX_COLOR);
		vboMesh->addAttrib(VERTEX_TEXTURE);
		vboMesh->bindAttribs();

		delete[] data;

        m_vbosSolid.push_back(vboMesh);        
    }

    delete model;
}

void Object::reloadShader()
{	
	delete m_shader;
	m_shader = new Shader("../Shader/Default33.vert.glsl", "../Shader/Default33.frag.glsl");
}